﻿using System;

namespace MathService.DSP
{
    /// <summary>Линия задержки</summary>
    [Serializable]
    public class DelayLine : IInitializable
    {
        /// <summary>Память линии задержки</summary>
        private double[] f_z;

        /// <summary>Указатель на первый элемент с области памяти линии задержки</summary>
        private int f_k; 

        /* -------------------------------------------------------------------------------------------- */

        /// <summary>Указатель на первую ячейку памяти в линии задержки</summary>
        protected int K
        {
            get { return f_k; }
            set { value %= Length; f_k = value; }
        }

        /// <summary>Значения в линии задержки</summary>
        /// <summary>Длина линии задержки</summary>        
        public int Length { get { return f_z.Length; } }

        /// <param name="i">Номер элемента линии</param>
        /// <returns>Значение элемента в линии задержки</returns>
        public double this[int i]
        {
            get { return f_z[GetAddress(i)]; }
            set { f_z[GetAddress(i)] = value; }
        }

        /// <summary>Первый элемент линии задержки</summary>
        public double First
        {
            get { return this[0]; }
            set { this[0] = value; }
        }

        /// <summary>Последний элемент линии задержки</summary>
        public double Last
        {
            get { return this[Length - 1]; }
            set { this[Length - 1] = value; }
        }

        /* -------------------------------------------------------------------------------------------- */

        /// <summary>Новая линия задержки</summary>
        /// <param name="Length">Длина линии задержки</param>
        public DelayLine(int Length)
        {
            f_z = new double[Length];
        }

        /* -------------------------------------------------------------------------------------------- */

        /// <summary>Инициализировать линию задержки - обнуление всех значений</summary>
        public void Initialize()
        {
            lock(this)
            {
                f_z = new double[f_z.Length];
                f_k = 0;
            }
        }

        /* -------------------------------------------------------------------------------------------- */

        /// <summary>Получить адрес элемента в области памяти линии задержки через порядковый номер</summary>
        /// <param name="i">Порядковый номер элемента в линии задержки</param>
        /// <returns>Значение элемента в линии задержки</returns>
        private int GetAddress(int i)
        {
            i = K - i - 1;
            if(i < 0) i += Length;
            return i;
        }

        /// <summary>Сдвинуть линию на один элемент вперёд</summary>
        public void Shift()
        {
            K++;
        }

        /// <summary>Сдинуть линию на n элементов вперёд</summary>
        /// <param name="n">Размер сдвига</param>
        public void Shift(int n)
        {
            K += n;
        }

        /// <summary>сдинуть линию на один элемент назад</summary>
        public void Return()
        {
            K--;
        }

        /// <summary>Сдинуть линию на n элементов назад</summary>
        /// <param name="n">Размер сдвига</param>
        public void Return(int n)
        {
            K -= n;
        }

        /// <summary>Добавить элемент в линию задержки первым со смещением всех элементов вперёд</summary>
        /// <param name="value">Добавляемое значение</param>
        public void AddFirst(double value)
        {
            lock(this)
            {
                Shift();
                First = value;
            }
        }

        /// <summary>Добавить элемент в линию задержки последним со смещением всех жлементов назад</summary>
        /// <param name="value">Добавляемое значение</param>
        public void AddLast(double value)
        {
            lock(this)
            {
                Return();
                Last = value;
            }
        }

        /* -------------------------------------------------------------------------------------------- */

        /// <summary>Получить Хэш-код объекта</summary>
        /// <returns>Хэш-код объекта</returns>
        public override int GetHashCode()
        {
            return f_z.GetHashCode();
        }

        /// <summary>Сравнить текущий объект с...</summary>
        /// <param name="obj">Сравниваемый объект</param>
        /// <returns>Линии считаются равными, если они одинаковой длины и значения их элементов равны</returns>
        public override bool Equals(object obj)
        {
            var lv_Line = obj as DelayLine;
            if(obj == null) return false;
            var lv_Length = this.Length;
            if(lv_Line == null || lv_Line.Length != lv_Length) return false;
            for(var i = 0; i < lv_Length; i++)
                if(lv_Line[i] != this[i]) return false;
            return true;

        }

        public override string ToString()
        {
            return "Delay line Length:" + Length;
        }

        /* -------------------------------------------------------------------------------------------- */


    }
}
